#ifndef CNPIPE_PIPERANGEDEFINER_H
#define CNPIPE_PIPERANGEDEFINER_H

#include "cnpipe_global.h"
#include "cnpipe_singlenode.h"
#include "cnpipe_singleelement.h"
#include "geometry/vertex3D.h"

#include <QDebug>
#include <QObject>


/**
 * 专门用于设置管道显示范围的管理器
 * @brief The CNPIPE_pipeRangeDefiner class
 */
class CNPIPE_pipeRangeDefiner : public QObject {
    Q_OBJECT
public:
    CNPIPE_pipeRangeDefiner (CNPIPE_global *par) {
        this->parent = par;
    }
    ~CNPIPE_pipeRangeDefiner() {

    }
    /** 初始化管道，并将管道设置在显示范围之内 */
    virtual void initializePipeForDisplaying(
            QList<CNPIPE_singleNode*> *nodesArray1, QList<CNPIPE_singleElement*> *element) {

        nodesArray = nodesArray1;
        elementsArray = element;
        nodesCache->clear();
        oneNodeMoreElements->clear();

        minX = 0; maxX = 0;
        minY = 0; maxY = 0;
        minZ = 0; maxZ = 0;
        addRecord = 0;

        int elementCount = element->size();

        for (int e = 0; e < elementCount; e++) {
            CNPIPE_singleElement *singleElement = element->at(e);
            QString node1Name = singleElement->getNode1();
            QString node2Name = singleElement->getNode2();

            CNPIPE_singleNode *node1 = getSingleNode(node1Name);
            CNPIPE_singleNode *node2 = getSingleNode(node2Name);

            AdjustRangeByAddSingleNode(node1);
            AdjustRangeByAddSingleNode(node2);

            AddSingleElementForNodes(singleElement);
        }

        XLength = maxX - minX;
        YLength = maxY - minY;
        ZLength = maxZ - minZ;

        qDebug() << "XLength:" << XLength;
        qDebug() << "YLength:" << YLength;
        qDebug() << "ZLength:" << ZLength;
    }
    virtual double getMinX() {
        return minX;
    }
    virtual double getMaxX() {
        return maxX;
    }
    virtual double getMinY() {
        return minY;
    }
    virtual double getMaxY() {
        return maxY;
    }
    virtual double getMinZ() {
        return minZ;
    }
    virtual double getMaxZ() {
        return maxZ;
    }
    virtual double getXLength() {
        return XLength;
    }
    virtual double getYLength() {
        return YLength;
    }
    virtual double getZLength() {
        return ZLength;
    }
    //获取默认的摄像机焦点位置
    virtual Vertex3D *getDefaultLookAtVertex() {
        double nX = minX + (maxX - minX) / 2,
               nY = minY + (maxY - minY) / 2,
               nZ = minZ + (maxZ - minZ) / 2;
        return AdjustSingleNodeVertexWithinRange(nX, nY, nZ);
    }
    //获取默认的摄像机位置
    virtual Vertex3D *getDefaultCameraPosition() {
        Vector3D *cameraDirection = new Vector3D(1, -1, 1);
        return getDefaultLookAtVertex()->addVectors(cameraDirection->multiply(2000));
    }
    virtual QList<CNPIPE_singleElement*> *getElementsArray() {
        return elementsArray;
    }
    /** 获取私有变量，代表一个节点包含几个单元的变量，这便于查看有多少个单元经过某一个节点 */
    virtual QMap<QString, QMap<QString, CNPIPE_singleElement *> *> *getOneNodeMoreElements() {
        return oneNodeMoreElements;
    }
    /** 根据一个节点号，获取新的节点信息。
     * 此外，为了下次更快捷地获取相同的节点信息，
     * 可以从缓存中获取节点。
     * 此方法为公有方法，便于生成实体管道时使用 */
    virtual CNPIPE_singleNode *getSingleNode(QString nodeName) {
        int size = nodesArray->size();
        if (nodesCache->value(nodeName)) {
            return nodesCache->value(nodeName);
        } else {
            for (int s = 0; s < size; s++) {
                CNPIPE_singleNode *node = nodesArray->at(s);
                nodesCache->insert(nodeName, node);
                if (node->getNode() == nodeName) {
                    return node;
                }
            }
        }
        return NULL;
    }
    /** 为了校正显示中的偏差，将节点调整成显示范围之内的坐标 */
    virtual Vertex3D *AdjustSingleNodeWithinRange(CNPIPE_singleNode *node) {
        return AdjustSingleNodeVertexWithinRange(node->getX(), node->getY(), node->getZ());
    }
    /** 为了校正显示中的偏差，将节点调整成显示范围之内的坐标 */
    virtual Vertex3D *AdjustSingleNodeNameWithinRange(QString nodeName) {
        CNPIPE_singleNode *node = getSingleNode(nodeName);
        if (node != NULL) {
            return AdjustSingleNodeWithinRange(node);
        } else {
            return NULL;
        }
    }
    /** 从一个单元中获取两个节点的信息 */
    virtual QMap<QString, CNPIPE_singleNode*> *getBothNodesFromSingleElement(CNPIPE_singleElement *element) {
        QString node1Name = element->getNode1();
        QString node2Name = element->getNode2();

        CNPIPE_singleNode *node1 = getSingleNode(node1Name);
        CNPIPE_singleNode *node2 = getSingleNode(node2Name);

        QMap<QString, CNPIPE_singleNode*> *returnMap = new QMap<QString, CNPIPE_singleNode*>();
        returnMap->insert(node1Name, node1);
        returnMap->insert(node1Name, node2);

        return returnMap;
    }
private:
    CNPIPE_global *parent;
    const double MAX_RANGE = 2000;
    double minX = 0, minY = 0, minZ = 0,
           maxX = 0, maxY = 0, maxZ = 0;
    double XLength = 1, YLength = 1, ZLength = 1;

    /** 统计显示范围的数目 */
    double addRecord = 0;


    /** 节点数组 */
    QList<CNPIPE_singleNode*> *nodesArray;
    /** 单元数组 */
    QList<CNPIPE_singleElement*> *elementsArray;
    /** 节点缓存，是为了快速根据节点号查找节点 */
    QMap<QString, CNPIPE_singleNode*> *nodesCache = new QMap<QString, CNPIPE_singleNode*>();
    /** 代表一个节点包含几个单元的变量，这便于查看有多少个单元经过某一个节点 */
    QMap<QString, QMap<QString, CNPIPE_singleElement*>*> *oneNodeMoreElements
        = new QMap<QString, QMap<QString, CNPIPE_singleElement*>*>();


    /** 添加一个单元，统计有多少个单元经过一个节点
     * (Add single element to see how many elements pass through single node) */
    virtual void AddSingleElementForNodes(CNPIPE_singleElement *element) {
        QString node1Name = element->getNode1();
        QString node2Name = element->getNode2();
        AddSingleNodeForElementCalculation(node1Name, element);
        AddSingleNodeForElementCalculation(node2Name, element);
    }

    /** 往oneNodeMoreElements中添加一个节点和单元，以确定有多少个单元经过一个节点 */
    virtual void AddSingleNodeForElementCalculation(QString nodeName, CNPIPE_singleElement *element) {
        QMap<QString, CNPIPE_singleElement*> *elementsOfSingleNode;

        if (oneNodeMoreElements->value(nodeName)) {//有节点号的记录
            elementsOfSingleNode = oneNodeMoreElements->value(nodeName);
        } else {//无节点号的记录
            elementsOfSingleNode = new QMap<QString, CNPIPE_singleElement*>();
            oneNodeMoreElements->insert(nodeName, elementsOfSingleNode);
        }

        QString elementName = element->getElementName();
        elementsOfSingleNode->insert(elementName, element);
    }

    /** 添加一个节点，调整显示范围 */
    virtual void AdjustRangeByAddSingleNode(CNPIPE_singleNode *node) {
        double nodeX = node->getX(), nodeY = node->getY(), nodeZ = node->getZ();
        if (addRecord == 0) {
            minX = nodeX; maxX = nodeX;
            minY = nodeY; maxY = nodeY;
            minZ = nodeZ; maxZ = nodeZ;
        }

        if (minX > nodeX) minX = nodeX;
        if (maxX < nodeX) maxX = nodeX;

        if (minY > nodeY) minY = nodeY;
        if (maxY < nodeY) maxY = nodeY;

        if (minZ > nodeZ) minZ = nodeZ;
        if (maxZ < nodeZ) maxZ = nodeZ;

        addRecord++;
    }

    virtual Vertex3D *AdjustSingleNodeVertexWithinRange(double nX, double nY, double nZ) {
        double maxRange = 0;
        if (XLength > YLength) maxRange = XLength;
        else maxRange = YLength;

        if (maxRange < ZLength) maxRange = ZLength;

        double rX = (nX - minX) / XLength * XLength / maxRange * MAX_RANGE;
        double rY = (nY - minY) / YLength * YLength / maxRange * MAX_RANGE;
        double rZ = (nZ - minZ) / ZLength * ZLength / maxRange * MAX_RANGE;

        return new Vertex3D(rX, rY, rZ);
    }
};

#endif // CNPIPE_PIPERANGEDEFINER_H
